<template>
  <div ref="editorRef" id="editor"></div>
</template>

<script setup lang="ts">
/** JS */
import {ref, defineProps, defineEmits, onMounted, onBeforeUnmount, nextTick, watch, Ref} from 'vue';
import Cache from '@/utils/Cache';
import {ResponseResult} from '@/utils/ApiRequest';
import toast from '@/utils/Toast';
/** 组件 */
import Vditor from 'vditor';

const cache = new Cache(), $ = require('jquery');
/** 钩子函数 props 和 emits */
const props = defineProps({
      // 默认显示的文字
      placeholder: {
        type: String,
        default: '请输入 Markdown'
      },
      // 高度
      height: {
        type: String,
        default: '360px'
      },
      // 宽度
      width: {
        type: String,
        default: '900px'
      },
      // 工具栏
      toolbar: {
        type: Array,
        default: () => [
          "emoji",
          "headings",
          "bold",
          "italic",
          "strike",
          "link",
          "|",
          "list",
          "ordered-list",
          "check",
          "outdent",
          "indent",
          "|",
          "quote",
          "line",
          "code",
          "inline-code",
          "insert-before",
          "insert-after",
          "|",
          "upload",
          "record",
          "table",
          "|",
          "undo",
          "redo",
          "|",
          "edit-mode",
          {
            name: "more",
            toolbar: [
              "both",
              "code-theme",
              "content-theme",
              "export",
              "outline",
              "preview",
            ],
          },
        ]
      },
      // 追加工具栏
      addToolbar: {
        type: Array,
        default: (): any[] => []
      },
      // 编辑框的数据
      editorValue: {
        type: String,
        default: ''
      },
      // 编辑器默认值
      defaultEditorValue: {
        type: String,
        default: ''
      },
      // 大纲
      outline: {
        type: Boolean,
        default: true
      },
      // 限制图片最大的大小
      imgMaxHeight: {
        type: String,
      },
      imgMaxWidth: {
        type: String,
      },
      // 图片默认大小
      imgDefaultHeight: {
        type: String,
      },
      imgDefaultWidth: {
        type: String,
      },
    }),
    emits = defineEmits(['change']);

/** 初始化 vditor */
let vditor = ref(), md = ref(''), html = ref(''), tb = ref(props.toolbar), editorRef = ref(null);
let defaultEditorText = ref(props.defaultEditorValue);
watch(md, (n) => {
  emits('change', {md: n, html: html.value});
});
// 图片计数器
let currentImgCount = 0, renderImgCount = 0;
watch(() => props.editorValue, (n) => {
  if (vditor.value) {
    vditor.value.setValue(n);
    md.value = n;
    // 更新图片大小的细节
    let imgs = document.getElementsByTagName('img');
    let pattern = /{{\d+,(\s)*\d+}}/;
    if (imgs && imgs.length > 0) {
      for (let img of imgs) {
        let imgParentEle = img.parentElement;
        if (imgParentEle) {
          let innerHtml = imgParentEle.innerHTML
          if (innerHtml) {
            let matchs = innerHtml.trim().match(pattern);
            if (matchs) {
              for (let match of matchs) {
                if (match && match.trim() !== '') {
                  // 获取高度进行筛选过滤
                  let matchStr = match.replace('{{', '').replace('}}', ''), matchArr = matchStr.split(',');
                  let height = matchArr[1], width = matchArr[0];
                  // 赋值 style
                  img.style.height = `${height}px`;
                  img.style.width = `${width}px`;
                  // 清除 {{number, number}}
                  imgParentEle.innerHTML = imgParentEle.innerHTML.replace(match, '');
                  let alt = img.getAttribute('alt');
                  if (alt) {
                    img.setAttribute('id', `img-${alt}-${renderImgCount}`);
                    imgParentEle.innerHTML += `<span id="position-${alt}-${renderImgCount}" style="display: none;">{{${height}, ${width}}}</span>`;
                  }
                  renderImgCount++;
                }
              }
            }
          }

        }
      }
    }
  }
});
watch(() => props.defaultEditorValue, (n) => {
  defaultEditorText.value = n;
})


onMounted(() => {
  // 初始化工具栏
  if (props.addToolbar.length > 0) {
    let newToolbar = tb.value.slice(0, tb.value.length - 3), endTool = tb.value.slice(tb.value.length - 3);
    newToolbar.push(...props.addToolbar);
    // 确保更多等选项保留在最后
    newToolbar.push(...endTool);
    tb.value = newToolbar;
  }
  // 创建编辑器
  // cdn: 'http://cdn.yeziji.xyz/vditor@3.8.15',
  nextTick(() => {
    let editor: Vditor;
    vditor.value = (editor = new Vditor("editor", {
      height: props.height,
      width: props.width,
      undoDelay: 0,
      value: defaultEditorText.value,
      mode: 'wysiwyg',
      cdn: 'https://cdn.yeziji.xyz/vditor@3.8.15',
      toolbarConfig: {
        hide: props.toolbar.length === 0,
        pin: false,
      },
      toolbar: (tb.value as (any)[]),
      outline: {
        // 开启大纲
        enable: props.outline,
        position: 'left'
      },
      preview: {
        hljs: {
          style: 'dracula',
          lineNumber: true
        },
        delay: 0
      },
      cache: {
        enable: false,
      },
      placeholder: props.placeholder,
      upload: {
        accept: 'image/*,.mp3, .wav, .rar',
        url: '/forum/uploadFile/dupload',
        linkToImgUrl: '/forum/uploadFile/dupload',
        withCredentials: true,
        fieldName: 'files',
        multiple: true,
        extraData: {
          "target": "detail"
        },
        setHeaders() {
          return {'Authorization': cache.get('token')};
        },
        format(files: File[], responseText: string) {
          let json: ResponseResult<string[]> = JSON.parse(responseText);
          let imgs: string[] = json.data;
          let succMap = {};
          for (let img of imgs) {
            succMap[img.substring(img.lastIndexOf('/') + 1)] = img;
          }
          return JSON.stringify({
            msg: json.msg,
            code: json.code,
            data: {
              errFiles: [],
              succMap
            }
          });
        },
        linkToImgFormat(responseText) {
          let json: ResponseResult<string[]> = JSON.parse(responseText);
          let imgs: string[] = json.data;
          let succMap = {};
          for (let img of imgs) {
            succMap[img.substring(img.lastIndexOf('/') + 1)] = img;
          }
          return JSON.stringify({
            msg: json.msg,
            code: json.code,
            data: {
              originalURL: json.data,
              url: json.data
            }
          });
        },
        filename(name) {
          return name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5\.)]/g, '').replace(/[\?\\/:|<>\*\[\]\(\)\$%\{\}@~]/g, '').replace('/\\s/g', '')
        },
      },
      async input() {
        if (editor) {
          let editorHtml = editor.getHTML();
          md.value = editor.getValue();
          html.value = editorHtml;
          if (editorRef.value && (props.imgDefaultHeight && props.imgDefaultWidth)) {
            const element = editorRef.value as HTMLElement;
            let images = element.getElementsByTagName('img');
            if (images && images.length > 0) {
              for (let image of images) {
                if (image.getAttribute('height') == null) {
                  // 说明没有改变过其大小
                  buildImage(image, props.imgDefaultHeight.replace('px', '').trim(), props.imgDefaultWidth.replace('px', '').trim());
                }
              }
            }
          }
        }
      },
      after() {
        // 监听点击事件，监听点击 img
        if (editorRef.value) {
          let element = editorRef.value as HTMLElement;
          element.onclick = function (event) {
            if (event?.target?.nodeName === 'IMG') {
              // 追加输入框
              let target: HTMLAnchorElement = event.target as HTMLAnchorElement;
              let vditorPanel = document.getElementsByClassName('vditor-panel vditor-panel--none');
              let imgPanel = vditorPanel[0];
              // 创建 span
              let heightAndWidthSpan = document.createElement('span');
              heightAndWidthSpan.setAttribute('class', 'vditor-tooltipped vditor-tooltipped__n');
              heightAndWidthSpan.setAttribute('aria-label', '宽x高');
              // 创建 input
              let heightAndWidthInput = document.createElement('input');
              heightAndWidthInput.setAttribute('class', 'vditor-input');
              heightAndWidthInput.setAttribute('placeholder', '输入宽x高');
              // 判断是否有过值
              let imgId = target.getAttribute('id');
              let positionDoc: HTMLElement | null = null;
              // 没 id 也有可能是初始化存在 {{width, height}}
              let parentEle = target.parentElement;
              if (parentEle) {
                for (let child of parentEle.children) {
                  if (child && child !== target) {
                    positionDoc = child as HTMLElement | null;
                  }
                }
              }
              // 初始化配置
              if (positionDoc !== null) {
                let oldInnerText = positionDoc.innerText,
                    oldInnerStr = oldInnerText.replace('{{', '').replace('}}', '');
                let oldInnerArr = oldInnerStr.split(','),
                    height = oldInnerArr[1],
                    width = oldInnerArr[0];
                heightAndWidthInput.value = `${width}x${height}`.trim();
              }
              // span 追加 input
              heightAndWidthSpan.appendChild(heightAndWidthInput);
              // imgPanel 追加 heightAndWidthSpan
              imgPanel.appendChild(heightAndWidthSpan);
              // 回车更新
              heightAndWidthInput.onchange = () => {
                let val = heightAndWidthInput.value;
                let hw = val.split('x');
                let tempHeight = Number(hw[1]), tempWidth = Number(hw[0]);
                // props.imgMax... 可能为 null，所以要再判断 max... 是否存在
                if (props.imgMaxHeight && props.imgMaxWidth) {
                  let maxHeight = Number(props.imgMaxWidth.replace('px', '').trim()),
                      maxWidth = Number(props.imgMaxHeight.replace('px', '').trim());
                  if ((maxHeight && tempHeight > maxHeight) || (maxWidth && tempWidth > maxWidth)) {
                    toast.error('不行的哟', `高度不能超过${maxHeight}的同时宽度不能超过${maxWidth}哦~`, 3000);
                    return;
                  }
                }
                if (tempHeight <= 0 || tempWidth <= 0) {
                  toast.error('提示提示', '高度和宽度最少要大于0哦~', 3000);
                } else if (target) {
                  buildImage(target, tempHeight, tempWidth);
                }
              }
            }
          }
        }
        // 如果工具栏为空则彻底删除 dom 元素，否则会显示个无元素的 toolbar
        if (props.toolbar?.length === 0 && props.addToolbar?.length === 0) {
          let hideToolbar = document.getElementsByClassName('vditor-toolbar--hide');
          $(hideToolbar).remove();
        }
      },
      tab: '\t'
    }));

    /**
     * 构建指定大小的图片
     * @param target 图片对象
     * @param tempHeight 图片高度
     * @param tempWidth 图片宽度
     */
    function buildImage(target: HTMLElement, tempHeight: number | string, tempWidth: number | string) {
      let parentEle = target.parentElement;
      target.style.height = `${tempHeight}px`;
      target.style.width = `${tempWidth}px`;
      let alt = target.getAttribute('alt'),
          positionId = `position-${alt}-${currentImgCount}`;
      target.setAttribute('id', `img-${alt}-${currentImgCount}`);
      if (parentEle) {
        let targetId = target.getAttribute('id');
        if (targetId) {
          // 修改过图片的大小
          for (let child of parentEle.children) {
            if (child && child !== target) {
              // 已存在 position - span
              parentEle.removeChild(child);
            }
          }
        }
        parentEle.innerHTML += `<span id="${positionId}" style="display: none;">{{${tempWidth},${tempHeight}}}</span>`;
        // 更新值
        md.value = editor.getValue();
        html.value = editor.getHTML();
        // 更新计数器
        currentImgCount++;
      }
    }
  });
});


onBeforeUnmount(() => {
  // 销毁编辑器
  if (vditor.value) {
    vditor.value.destroy();
    vditor.value = undefined;
  }
});
</script>